package com.xiaoshuidi.cloud.module.report.utils;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.Months;
import org.springframework.util.Assert;

import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.function.BiFunction;

@Slf4j
public class DateUtils {
    private static SimpleDateFormat MONTH_SDF = new SimpleDateFormat("yyyy-MM");

    public static List<String> getYearMonthListByStartAndEndDate(LocalDate start, LocalDate end) {
        List<String> yearMonths = new ArrayList<>();
        yearMonths.add(start.toString().substring(0,7));
        while (start.isBefore(end) && !start.toString().substring(0,7).equals(end.toString().substring(0,7))){
            start = start.plusMonths(1);
            yearMonths.add(start.toString().substring(0,7));
        }
        return yearMonths;
    }

    /**
     * 获取上月起止日期 截止到天
     */
    public static Map<String,Date> getLastOneMonthDay(){
        Map<String, Date> map = new HashMap<>();
        // 上月起始
        Calendar lastMonthFirstDateCal = Calendar.getInstance();
        lastMonthFirstDateCal.add(Calendar.MONTH, -1);
        lastMonthFirstDateCal.set(Calendar.DAY_OF_MONTH, 1);
        Date lastMonthFirstTime = lastMonthFirstDateCal.getTime();
        // 上月末尾
        Calendar lastMonthEndDateCal = Calendar.getInstance();
        lastMonthEndDateCal.add(Calendar.MONTH, -1);
        lastMonthEndDateCal.set(
                Calendar.DAY_OF_MONTH, lastMonthEndDateCal.getActualMaximum(Calendar.DAY_OF_MONTH));
        Date lastMonthEndTime = lastMonthEndDateCal.getTime();
        map.put("beginDate", lastMonthFirstTime);
        map.put("endDate", lastMonthEndTime);
        return map;
    }


    /**
     * 获取上月起止日期 截止到时分秒
     */
    public static Map<String, Date> getLastOneMonthDayTime() {
        Map map = null;
        try {
            Long startTime = getLastMonthStartTime();
            Long endTime = getLastMonthEndTime();
            LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(startTime), ZoneId.systemDefault());
            LocalDateTime localDateTime1 = LocalDateTime.ofInstant(Instant.ofEpochMilli(endTime), ZoneId.systemDefault());
            map = new HashMap();

            map.put("startTime", localDateTime);
            map.put("endTime", localDateTime1);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("getLastOneMonthDayTime error{}",e.getMessage());
        }
        return map;
    }

    // 获取当天的开始时间
    public static Date getDayBegin() {

        Calendar cal = new GregorianCalendar();
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }

    // 获取当天的结束时间
    public static Date getDayEnd() {

        Calendar cal = new GregorianCalendar();
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        return cal.getTime();
    }

    // 获取昨天的开始时间
    public static LocalDateTime getBeginDayOfYesterday(int i) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(getDayBegin());
        cal.add(Calendar.DAY_OF_MONTH, -i);
        return LocalDateTime.ofInstant(cal.toInstant(), ZoneOffset.systemDefault());

    }

    // 获取昨天的结束时间
    public static LocalDateTime getEndDayOfYesterDay(int i) {
        Calendar cal = new GregorianCalendar();
        cal.setTime(getDayEnd());
        cal.add(Calendar.DAY_OF_MONTH, -i);
        return LocalDateTime.ofInstant(cal.toInstant(), ZoneOffset.systemDefault());

    }

    public static Long getLastMonthStartTime() throws Exception {
        Long currentTime = System.currentTimeMillis();

        String timeZone = "GMT+8:00";
        Calendar calendar = Calendar.getInstance();// 获取当前日期
        calendar.setTimeZone(TimeZone.getTimeZone(timeZone));
        calendar.setTimeInMillis(currentTime);
        calendar.add(Calendar.YEAR, 0);
        calendar.add(Calendar.MONTH, -1);
        calendar.set(Calendar.DAY_OF_MONTH, 1);// 设置为1号,当前日期既为本月第一天
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        return calendar.getTimeInMillis();
    }


    public static Long getLastMonthEndTime() {
        Long currentTime = System.currentTimeMillis();

        String timeZone = "GMT+8:00";
        Calendar calendar = Calendar.getInstance();// 获取当前日期
        calendar.setTimeZone(TimeZone.getTimeZone(timeZone));
        calendar.setTimeInMillis(currentTime);
        calendar.add(Calendar.YEAR, 0);
        calendar.add(Calendar.MONTH, -1);
        calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));// 获取当前月最后一天
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.MILLISECOND, 999);

        return calendar.getTimeInMillis();
    }

    /**
     * 获取 根据当前月和num计算出的所在月的天数
     * @param num
     * @return
     */
    public static int getDaysOfMonth(int num) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MONTH,num);
        return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    /**
     * 获取时间的天数
     * @param date
     * @return
     */
    public static int getDaysNow(LocalDate date) {
        return date.getDayOfMonth();
    }



    /**
     * 将日期转为指定的string
     */
    public static String getDateString(Date date){
        return MONTH_SDF.format(date);
    }


    /**
     * 将日期转为指定的string
     */
    public static Date getDateByString(String date) throws Exception{
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.parse(date);
    }

    /**
     * 比较date和上个月的月份是否相等
     */
    public static Boolean compareMonthOnLastMonth(LocalDate date){
        String dateMonth = MONTH_SDF.format(date);
        Date now = new Date();
        Calendar cal = Calendar.getInstance();
        cal.setTime(now);
        cal.add(Calendar.MONTH,-1);
        String lastMonth =MONTH_SDF.format(cal.getTime());
        return dateMonth.equals(lastMonth);
    }

    public static int getMonthSpace(LocalDate startTime, LocalDate endTime) {
        int year = endTime.getYear()-startTime.getYear();
        int months = year * 12 +(endTime.getMonthValue()-startTime.getMonthValue());
        return months;
    }

    public static long getDaySpace(LocalDate startTime, LocalDate endTime) {
        return endTime.toEpochDay() - startTime.toEpochDay();
    }

    public static String getLastMonth(){
        LocalDate now = LocalDate.now();
        LocalDate lastMonth = now.minusMonths(1);
        DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy年MM月");
        return dateFormat.format(lastMonth);
    }
    public static String getCurrentMonth(){
        LocalDate now = LocalDate.now();
        DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy年MM月");
        return dateFormat.format(now);
    }
    public static String getYesterdayMonth(){
        LocalDate yesterday = LocalDate.now().minusDays(1);
        DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy年MM月");
        return dateFormat.format(yesterday);
    }
    public static String getYesterdayMonthAnI(int i){
        LocalDate yesterday = LocalDate.now().minusDays(i);
        DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy年MM月");
        return dateFormat.format(yesterday);
    }
    public static String getYesterdayLastMonthAnI(int i){
        LocalDate yesterday = LocalDate.now().minusDays(i).minusMonths(1);
        DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy年MM月");
        return dateFormat.format(yesterday);
    }

    public static void main(String[] args) {
        String yesterday = getYesterdayMonthAnI(76);
        System.out.println(yesterday);
    }

    /**
     * 判断是否在同一个月
     * @return
     */
    public static boolean inMonth(LocalDate yesterday,LocalDate startTime){
        Calendar calendar1 = Calendar.getInstance();
        calendar1.setTime(Date.from(yesterday.atStartOfDay(ZoneId.systemDefault()).toInstant()));
        Calendar calendar2 = Calendar.getInstance();
        calendar2.setTime(Date.from(startTime.atStartOfDay(ZoneId.systemDefault()).toInstant()));
        return calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR) && calendar1.get(Calendar.MONTH) == calendar2.get(Calendar.MONTH);
    }

    public static MonthAndRemainder monthsAndRemainder(LocalDate contractStartDate, LocalDate contractEndDate) {
        return new MonthAndRemainder(contractStartDate, contractEndDate);
    }

    /**
     * 二月是否在时间段内(不包括开始月份)
     *
     * @param startTime
     * @param endTime
     * @return
     */
    public static boolean isFebruaryWithin(LocalDate startTime, LocalDate endTime) {
        if (startTime.isAfter(startTime.withMonth(2)) && endTime.getYear() > startTime.getYear()
                && endTime.getMonthValue() > 2) {
            return true;
        }
        return startTime.isBefore(startTime.withMonth(2)) && endTime.isAfter(endTime.withMonth(2));
    }

    /**
     * 账单的结束日期(开始时间+1月-1天)
     * 如果2月在合同周期内(非首月), 并且账单开始时间是2月最后一天, 则此账单结束日期为结束日期当月的最后一天-1天
     * 例: 2/28 ~ 3/30, 2/28 ~ 4/29
     *
     * @param startTime                账单开始时间
     * @param isFebruaryWithinContract 二月是否在合同周期内
     * @param month                    开始时间加几个月
     * @return
     */
    public static LocalDate monthBillEndDate(LocalDate startTime, boolean isFebruaryWithinContract, int month) {
        month = Math.max(1, month);
        LocalDate endDate = startTime.plusMonths(month);
        if (endDate.getMonthValue() == 3 && isFebruaryWithinContract) {
            if (endDate.getDayOfMonth() == endDate.withMonth(2).lengthOfMonth()) {
                return endDate.withDayOfMonth(endDate.lengthOfMonth() - 1);
            }
        }
        return endDate.minusDays(1);
    }

    private static void assertNotNull(LocalDate startTime, LocalDate endTime) {
        Assert.notNull(startTime, "startTime cannot be null");
        Assert.notNull(endTime, "startTime cannot be null");
    }

    private static <T> T exchangeIf(LocalDate left, LocalDate right, BiFunction<LocalDate, LocalDate, T> consumer) {
        assertNotNull(left, right);
        if (left.isAfter(right)) {
            return consumer.apply(right, left);
        } else {
            return consumer.apply(left, right);
        }
    }


    /**
     * 周期账单天数
     *
     * @param startTime
     * @param endTime
     * @return
     */
    public static int totalDays(LocalDate startTime, LocalDate endTime) {
        return exchangeIf(startTime, endTime, (s, e) -> (int) ChronoUnit.DAYS.between(s, e) + 1);
    }

    public static class MonthAndRemainder {
        @Getter
        private int month;
        @Getter
        private int remainder;
        @Getter
        private boolean isFebruaryIn;

        /**
         * @param startDate
         * @param endDate
         */
        public MonthAndRemainder(LocalDate startDate, LocalDate endDate) {
            isFebruaryIn = isFebruaryWithin(startDate, endDate);
            exchangeIf(startDate, endDate, (s, e) -> {
                LocalDate monthEnd = monthBillEndDate(s, isFebruaryIn, 1);
                // 按账单逻辑刚好1整月
                if (monthEnd.compareTo(e) == 0) {
                    month = 1;
                    return null;
                    // 不足1个月
                } else if (e.compareTo(monthEnd) < 0) {
                    remainder = totalDays(s, e);
                    // 大于1个月
                } else {
                    LocalDate tmp;
                    for (int i = 1; ; i++) {
                        tmp = monthBillEndDate(s, isFebruaryIn, i);
                        if (tmp.compareTo(e) == 0) {
                            month++;
                            break;
                        } else if (tmp.compareTo(e) > 0) {
                            remainder = totalDays(monthBillEndDate(s, isFebruaryIn, i - 1).plusDays(1), e);
                            break;
                        }
                        month++;
                    }
                }
                return null;
            });
        }

        public int getYear() {
            return month / 12;
        }

        public boolean isEquals(MonthAndRemainder other) {
            if (other == null) {
                return false;
            }
            return month == other.getMonth() && remainder == other.getRemainder();
        }
    }

}
